/*
 * ble_services.c
 *
 *  Created on: 2018年5月5日
 *      Author: 0yfj0
 */

#include <string.h>
#include <time.h>

#include "app_lamp.h"
#include "board.h"
#include "sys.h"
#include "sys_env2.h"
#include "sys_auth.h"
#include "boards.h"
#include "sdk_macros.h"
#include "nrf_error.h"
#include "app_error.h"
#include "ble.h"
#include "ble_gap.h"
#include "ble_nus.h"
#include "ble_dis.h"
#include "ble_dfu.h"
#include "ble_conn_state.h"
#include "peer_manager.h"
#include "app_timer.h"
#include "nrf_log.h"
#include "ble_lamp.h"

/*----------------------------------------------------------------------------*/
static ble_curr_tim_t m_curr_tim;
static ble_env_sensors_t m_env;
static ble_nus_t m_nus;
static ble_lamp_t m_lamp;
#if SERVICE_DFU
static ble_dfu_t m_dfu;
#endif
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;

void ble_lamp_write_handler(ble_lamp_t *p_lamp, int lamp, uint8_t new_state);
void ble_nus_data_handler(ble_nus_t * p_nus, uint8_t *p_data, uint16_t length);
#if SERVICE_DFU
void ble_dfu_evt_handler(ble_dfu_t *p_dfu, ble_dfu_evt_t *p_evt);
#endif

uint32_t ble_lamp_init(ble_lamp_t *p_lamp, const ble_lamp_init_t *p_lamp_init);
uint32_t ble_current_time_init(ble_curr_tim_t *p_curr_tim);
uint32_t ble_env_sensors_init(ble_env_sensors_t *p_env_sensors);

void ble_disconnect(uint8_t hci_status_code);
/*----------------------------------------------------------------------------*/
/**@brief Function for initializing services that will be used by the application.
 */
void ble_lamp_services_init(void)
{
	/* YOUR_JOB: Add code to initialize the services used by the application. */
	uint32_t err_code;

	ble_dis_init_t dis_init;
	// Initialize Device Information Service.
	memset(&dis_init, 0, sizeof(dis_init));
	ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, MANUFACTURER_NAME);
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&dis_init.dis_attr_md.read_perm);
	BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init.dis_attr_md.write_perm);
	err_code = ble_dis_init(&dis_init);
	APP_ERROR_CHECK(err_code);

	memset(&m_curr_tim, 0, sizeof(m_curr_tim));
	err_code = ble_current_time_init(&m_curr_tim);
	APP_ERROR_CHECK(err_code);

	memset(&m_env, 0, sizeof(m_env));
	err_code = ble_env_sensors_init(&m_env);
	APP_ERROR_CHECK(err_code);

	// Initialize Nordic UART Service.
	memset(&m_nus, 0, sizeof(m_nus));
	ble_nus_init_t nus_init;
	memset(&nus_init, 0, sizeof(nus_init));
	nus_init.data_handler = ble_nus_data_handler;
	err_code = ble_nus_init(&m_nus, &nus_init);
	APP_ERROR_CHECK(err_code);

	lamp_init();

	// Initialize LED Service.
	memset(&m_lamp, 0, sizeof(m_lamp));
	ble_lamp_init_t lamps_init;
	memset(&lamps_init, 0, sizeof(lamps_init));
	lamps_init.lamp_write_handler = ble_lamp_write_handler;
	err_code = ble_lamp_init(&m_lamp, &lamps_init);
	APP_ERROR_CHECK(err_code);

#if SERVICE_DFU
	// dfu Service
	memset(&m_dfu, 0, sizeof(m_dfu));
	ble_dfu_init_t dfu_init;
	memset(&dfu_init, 0, sizeof(dfu_init));
	dfu_init.evt_handler = ble_dfu_evt_handler;
	err_code = ble_dfu_init(&m_dfu, &dfu_init);
	APP_ERROR_CHECK(err_code);
#endif

	ble_gatts_value_t val;
	memset(&val, 0, sizeof(val));
	err_code = sd_ble_gatts_value_get(BLE_CONN_HANDLE_INVALID,
			m_lamp.lamp1_char_handles.value_handle, &val);
	APP_ERROR_CHECK(err_code);

	uint8_t i;
	uint8_t data;
	uint16_t handle;
	if (val.p_value == NULL)
	{
		val.p_value = &data;
	}

	for (i = 0; i < LAMP_SZ; ++i)
	{
		val.p_value[0] = (uint8_t) app.stat.lamp[i];
		if (i == 0)
		{
			handle = m_lamp.lamp1_char_handles.value_handle;
		}
		else if (i == 1)
		{
			handle = m_lamp.lamp2_char_handles.value_handle;
		}
		else if (i == 2)
		{
			handle = m_lamp.lamp3_char_handles.value_handle;
		}
		else
		{
			handle = BLE_CONN_HANDLE_INVALID;
		}

		err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, handle,
				&val);
		APP_ERROR_CHECK(err_code);
		lamp_ctrl(i, val.p_value[0]);
	}
}

void ble_lamp_on_ble_evt(ble_evt_t *p_ble_evt)
{
	switch (p_ble_evt->header.evt_id)
	{
	case BLE_GAP_EVT_CONNECTED:
		m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
		if (sys.deny == 0)
		{
			sys.flag.auth = 0;/* 清除认证标志 */
			sys.disconn = BLE_T1S_AUTH;
		}
		else/* 拒绝连接断开此次连接 */
		{
			ble_disconnect(BLE_HCI_AUTHENTICATION_FAILURE);
		}
		break;
	case BLE_GAP_EVT_DISCONNECTED:
		m_conn_handle = BLE_CONN_HANDLE_INVALID;
		break;
//	case BLE_GAP_EVT_AUTH_STATUS:
//		if (p_ble_evt->evt.gap_evt.params.auth_status.auth_status
//				!= BLE_GAP_SEC_STATUS_SUCCESS)
//			ble_disconnect(BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
//		break;
	}

	ble_nus_on_ble_evt(&m_nus, p_ble_evt);
	ble_curr_tim_on_ble_evt(&m_curr_tim, p_ble_evt);
	ble_lamp_lamp_on_ble_evt(&m_lamp, p_ble_evt);
}

void ble_current_time_update(void)
{
	uint32_t err_code;
	ble_gatts_value_t val;

	memset(&val, 0, sizeof(val));
	err_code = sd_ble_gatts_value_get(BLE_CONN_HANDLE_INVALID,
			m_curr_tim.curr_tim_char_handles.value_handle, &val);
	APP_ERROR_CHECK(err_code);

	if (val.p_value == NULL)
	{
		val.p_value = app.current_time;
	}
	else
	{
		memcpy(val.p_value, app.current_time, val.len);
	}

	err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
			m_curr_tim.curr_tim_char_handles.value_handle, &val);
	APP_ERROR_CHECK(err_code);

	if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
	{
		uint16_t length;
		ble_gatts_hvx_params_t hvx_params;

		length = sizeof(app.current_time);
		memset(&hvx_params, 0, sizeof(hvx_params));
		hvx_params.handle = m_curr_tim.curr_tim_char_handles.value_handle;
		hvx_params.p_data = app.current_time;
		hvx_params.p_len = &length;
		hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
		err_code = sd_ble_gatts_hvx(m_conn_handle, &hvx_params);
	}
}

void ble_temperature_update(void)
{
	uint32_t err_code;
	ble_gatts_value_t val;

	memset(&val, 0, sizeof(val));
	val.len = 2;
	val.p_value = (uint8_t*) &app.tempx100;
	err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
			m_env.temp_char_handles.value_handle, &val);
	APP_ERROR_CHECK(err_code);
}

void ble_humidity_update(void)
{
	uint32_t err_code;
	ble_gatts_value_t val;

	memset(&val, 0, sizeof(val));
	val.len = 2;
	val.p_value = (uint8_t*) &app.humx100;
	err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
			m_env.hum_char_handles.value_handle, &val);
	APP_ERROR_CHECK(err_code);
}

/*----------------------------------------------------------------------------*/
void ble_lamp_write_handler(ble_lamp_t *p_lamp, int lamp, uint8_t new_state)
{
	lamp_ctrl(lamp, new_state);
}

void ble_nus_data_handler(ble_nus_t *p_nus, uint8_t *p_data, uint16_t length)
{
	char buf[32];

	if (sys.flag.auth == BLE_CONN_AUTH)
	{
		lamp_conf(p_data, length);
	}
	else if (memcmp(p_data, "pubkey=", 7) == 0)
	{
		if (strstr((char*) &p_data[7], sys.pubkey))
		{
			sys.flag.auth = BLE_CONN_AUTH;
			snprintf(buf, sizeof(buf), "pubkey=success");
			ble_nus_send(buf, strlen(buf));
		}
		else
		{
			snprintf(buf, sizeof(buf), "pubkey=error");
			ble_nus_send(buf, strlen(buf));
		}
	}
	else
	{
		uint32_t rand = auth_random();/* 获取随机数 */
		uint32_t pubkey = auth_pubkey(rand, sys.conf.passwd);
		snprintf(sys.pubkey, sizeof(sys.pubkey), "%lx", pubkey);
		snprintf(buf, sizeof(buf), "random=%lx", rand);
		ble_nus_send(buf, strlen(buf));
	}
}
void ble_nus_send(void *buf, uint16_t len)
{
	ble_nus_string_send(&m_nus, (uint8_t*) buf, len);
}
#if SERVICE_DFU
void ble_dfu_evt_handler(ble_dfu_t *p_dfu, ble_dfu_evt_t *p_evt)
{
	switch(p_evt->type)
	{
		case BLE_DFU_EVT_ENTERING_BOOTLOADER:
		case BLE_DFU_EVT_INDICATION_ENABLED:
		case BLE_DFU_EVT_INDICATION_DISABLED:
		break;
	}
}
#endif

/*----------------------------------------------------------------------------*/
static void on_write(ble_lamp_t *p_lamp, ble_evt_t *p_ble_evt)
{
	ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

	if ((p_evt_write->handle == p_lamp->lamp1_char_handles.value_handle)
			&& (p_evt_write->len == 1) && (p_lamp->lamp_write_handler != NULL))
	{
		p_lamp->lamp_write_handler(p_lamp, 0, p_evt_write->data[0]);
	}
	else if ((p_evt_write->handle == p_lamp->lamp2_char_handles.value_handle)
			&& (p_evt_write->len == 1) && (p_lamp->lamp_write_handler != NULL))
	{
		p_lamp->lamp_write_handler(p_lamp, 1, p_evt_write->data[0]);
	}
	else if ((p_evt_write->handle == p_lamp->lamp3_char_handles.value_handle)
			&& (p_evt_write->len == 1) && (p_lamp->lamp_write_handler != NULL))
	{
		p_lamp->lamp_write_handler(p_lamp, 2, p_evt_write->data[0]);
	}
}
void ble_lamp_lamp_on_ble_evt(ble_lamp_t *p_lamp, ble_evt_t *p_ble_evt)
{
	switch (p_ble_evt->header.evt_id)
	{
	case BLE_GATTS_EVT_WRITE:
		if (sys.flag.auth == BLE_CONN_AUTH)
			on_write(p_lamp, p_ble_evt);
		break;
	default:
		// No implementation needed.
		break;
	}
}
static uint32_t lamp_char_add(ble_lamp_t *p_lamp,
		const ble_lamp_init_t *p_lamp_init)
{
	uint32_t err_code;
	ble_gatts_char_md_t char_md;
	ble_gatts_attr_t attr_char_value;
	ble_uuid_t ble_uuid;
	ble_gatts_attr_md_t attr_md;

	memset(&char_md, 0, sizeof(char_md));
	char_md.char_props.read = 1;
	char_md.char_props.write = 1;
	char_md.p_char_user_desc = NULL;
	char_md.p_char_pf = NULL;
	char_md.p_user_desc_md = NULL;
	char_md.p_cccd_md = NULL;
	char_md.p_sccd_md = NULL;

	BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_DIGITAL);
	memset(&attr_md, 0, sizeof(attr_md));
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
//	BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&attr_md.write_perm);
//	BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&attr_md.write_perm);
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
	attr_md.vloc = BLE_GATTS_VLOC_STACK;
	attr_md.rd_auth = 0;
	attr_md.wr_auth = 0;
	attr_md.vlen = 0;
	memset(&attr_char_value, 0, sizeof(attr_char_value));
	attr_char_value.p_uuid = &ble_uuid;
	attr_char_value.p_attr_md = &attr_md;
	attr_char_value.init_len = sizeof(uint8_t);
	attr_char_value.init_offs = 0;
	attr_char_value.max_len = sizeof(uint8_t);
	attr_char_value.p_value = NULL;

	err_code = sd_ble_gatts_characteristic_add(p_lamp->service_handle, &char_md,
			&attr_char_value, &p_lamp->lamp1_char_handles);
	VERIFY_SUCCESS(err_code);
//	err_code = sd_ble_gatts_characteristic_add(p_lamp->service_handle, &char_md,
//			&attr_char_value, &p_lamp->lamp2_char_handles);
//	VERIFY_SUCCESS(err_code);
//	err_code = sd_ble_gatts_characteristic_add(p_lamp->service_handle, &char_md,
//			&attr_char_value, &p_lamp->lamp3_char_handles);
//	VERIFY_SUCCESS(err_code);

	return NRF_SUCCESS;
}
uint32_t ble_lamp_init(ble_lamp_t *p_lamp, const ble_lamp_init_t *p_lamp_init)
{
	uint32_t err_code;
	ble_uuid_t ble_uuid;

	VERIFY_PARAM_NOT_NULL(p_lamp);
	VERIFY_PARAM_NOT_NULL(p_lamp_init);

	// Initialize service structure.
	p_lamp->lamp_write_handler = p_lamp_init->lamp_write_handler;

	// Add service.
	BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_SERVICE_AUTOMATION_IO);

	err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid,
			&p_lamp->service_handle);
	VERIFY_SUCCESS(err_code);

	// Add characteristics.
	err_code = lamp_char_add(p_lamp, p_lamp_init);
	VERIFY_SUCCESS(err_code);

	return NRF_SUCCESS;
}
uint32_t ble_current_time_init(ble_curr_tim_t *p_curr_tim)
{
	uint32_t err_code;
	ble_uuid_t ble_uuid;
	ble_gatts_char_md_t char_md;
	ble_gatts_attr_t attr_char_value;
	ble_gatts_attr_md_t attr_md;

	VERIFY_PARAM_NOT_NULL(p_curr_tim);

	// Add service
	BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_CURRENT_TIME_SERVICE);

	err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid,
			&p_curr_tim->service_handle);
	VERIFY_SUCCESS(err_code);

	memset(&char_md, 0, sizeof(char_md));
	char_md.char_props.notify = 1;
	char_md.char_props.read = 1;
	char_md.char_props.write = 1;
	char_md.p_char_user_desc = NULL;
	char_md.p_char_pf = NULL;
	char_md.p_user_desc_md = NULL;
	char_md.p_cccd_md = NULL;
	char_md.p_sccd_md = NULL;
	ble_uuid.uuid = BLE_UUID_CURRENT_TIME_CHAR;
	memset(&attr_md, 0, sizeof(attr_md));
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
	BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&attr_md.write_perm);
	attr_md.vloc = BLE_GATTS_VLOC_STACK;
	attr_md.rd_auth = 0;
	attr_md.wr_auth = 0;
	attr_md.vlen = 0;
	memset(&attr_char_value, 0, sizeof(attr_char_value));
	attr_char_value.p_uuid = &ble_uuid;
	attr_char_value.p_attr_md = &attr_md;
	attr_char_value.init_len = sizeof(app.current_time);
	attr_char_value.init_offs = 0;
	attr_char_value.max_len = sizeof(app.current_time);
	attr_char_value.p_value = NULL;
	err_code = sd_ble_gatts_characteristic_add(p_curr_tim->service_handle,
			&char_md, &attr_char_value, &p_curr_tim->curr_tim_char_handles);
	VERIFY_SUCCESS(err_code);

	ble_current_time_update();

	return NRF_SUCCESS;
}
void ble_curr_tim_on_ble_evt(ble_curr_tim_t *p_curr_tim, ble_evt_t *p_ble_evt)
{
	switch (p_ble_evt->header.evt_id)
	{
	case BLE_GATTS_EVT_WRITE:
	{
		if (sys.flag.auth == BLE_CONN_AUTH)
		{
			ble_gatts_evt_write_t *p_evt_write =
					&p_ble_evt->evt.gatts_evt.params.write;
			if ((p_evt_write->handle
					== p_curr_tim->curr_tim_char_handles.value_handle)
					&& (p_evt_write->len == sizeof(app.current_time)))
			{
				struct tm t;
				memset(&t, 0, sizeof(t));
				t.tm_year = *((uint16_t*) &p_evt_write->data[0]) - 1900;
				t.tm_mon = p_evt_write->data[2] - 1;
				t.tm_mday = p_evt_write->data[3];
				t.tm_hour = p_evt_write->data[4];
				t.tm_min = p_evt_write->data[5];
				t.tm_sec = p_evt_write->data[6];
				sys.sec = mktime(&t);
			}
		}
		break;
	}
	default:
		// No implementation needed.
		break;
	}
}
uint32_t ble_env_sensors_init(ble_env_sensors_t *p_env_sensors)
{
	uint32_t err_code;
	ble_uuid_t ble_uuid;
	ble_gatts_char_md_t char_md;
	ble_gatts_attr_t attr_char_value;
	ble_gatts_attr_md_t attr_md;

	VERIFY_PARAM_NOT_NULL(p_env_sensors);

	// Add service
	BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_SERVICE_ENV_SENSORS);

	err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid,
			&p_env_sensors->service_handle);
	VERIFY_SUCCESS(err_code);

	memset(&char_md, 0, sizeof(char_md));
	char_md.char_props.read = 1;
	char_md.char_props.write = 0;
	char_md.p_char_user_desc = NULL;
	char_md.p_char_pf = NULL;
	char_md.p_user_desc_md = NULL;
	char_md.p_cccd_md = NULL;
	char_md.p_sccd_md = NULL;
	ble_uuid.uuid = BLE_UUID_TEMPERATURE;
	memset(&attr_md, 0, sizeof(attr_md));
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
	attr_md.vloc = BLE_GATTS_VLOC_STACK;
	attr_md.rd_auth = 0;
	attr_md.wr_auth = 0;
	attr_md.vlen = 0;
	memset(&attr_char_value, 0, sizeof(attr_char_value));
	attr_char_value.p_uuid = &ble_uuid;
	attr_char_value.p_attr_md = &attr_md;
	attr_char_value.init_len = 2;
	attr_char_value.init_offs = 0;
	attr_char_value.max_len = 2;
	attr_char_value.p_value = NULL;
	err_code = sd_ble_gatts_characteristic_add(p_env_sensors->service_handle,
			&char_md, &attr_char_value, &p_env_sensors->temp_char_handles);
	VERIFY_SUCCESS(err_code);

	ble_uuid.uuid = BLE_UUID_HUMIDITY;
	attr_char_value.p_value = NULL;
	err_code = sd_ble_gatts_characteristic_add(p_env_sensors->service_handle,
			&char_md, &attr_char_value, &p_env_sensors->hum_char_handles);
	VERIFY_SUCCESS(err_code);

	ble_temperature_update();
	ble_humidity_update();

	return NRF_SUCCESS;
}
/*----------------------------------------------------------------------------*/
void ble_disconnect(uint8_t hci_status_code)
{
	if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
	{
		sd_ble_gap_disconnect(m_conn_handle, hci_status_code);
		m_conn_handle = BLE_CONN_HANDLE_INVALID;
		sys.deny = BLE_T1S_DENY;
	}
}

void ble_timer1s_task(void)
{
	if (sys.deny > 0)
	{
		--sys.deny;
	}

	if (sys.disconn > 0 && --sys.disconn == 0)/* 延迟断开连接 */
	{
		if (sys.flag.auth != BLE_CONN_AUTH)
			ble_disconnect(BLE_HCI_AUTHENTICATION_FAILURE);
	}
}

/*----------------------------------------------------------------------------*/

